package org.exez.modules.visitor.service.impl;

import cn.hutool.http.HttpRequest;
import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSON;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.exez.common.exception.JeecgBootException;
import org.exez.common.util.DateUtils;
import org.exez.common.util.RedisUtil;
import org.exez.modules.system.util.VisitorBeanUtils;
import org.exez.modules.visitor.constant.ReserveTypeEnum;
import org.exez.modules.visitor.dto.terminal.AccompanyingPushDTO;
import org.exez.modules.visitor.dto.terminal.ReservationApplicationPushDTO;
import org.exez.modules.visitor.entity.*;
import org.exez.modules.visitor.service.*;
import org.exez.modules.visitor.sjy.ResultSjy;
import org.exez.modules.visitor.sjy.Success;
import org.exez.modules.visitor.util.AESSJY;
import org.exez.modules.visitor.util.UploadUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.Collectors;

@Slf4j
@Service
public class SjyServiceImpl implements SjyService {


    @Value("${url.mogo-get-base}")
    private String GET_BASE;
    /**
     * sjy用户名
     */
    public static final String userName = "admin";

    /**
     * sjy密码
     */
    public static final String password = "super";

    /**
     * sky key
     */
    public static final String key = "01f34g678h012o45";

    /**
     * 获取token
     */
    public static final String GET_TOKEN = "/sjy/token";

    /**
     * 访客预约
     */
    public static final String VISITOR = "/sjy/visitor/register";


    /**
     * 获取心跳
     */
    public static final String HEARTBEAT = "/sjy/tool/heartbeat";


    /**
     * 白名单修改
     */
    public static final String UPDATE = "/sjy/visitor/update";

    /**
     * 白名单删除
     */
    public static final String DELETE = "/sjy/visitor/delete/";


    /**
     * 添加
     */
    public static final int SAVE_TYPE = 1;

    /**
     * 修改
     */
    public static final int UPDATE_TYPE = 2;

    /**
     * 删除
     */
    public static final int DELETE_TYPE = 3;


    @Autowired
    private RedisUtil redisUtil;


    @Autowired
    private IPushRecordService pushRecordService;


    @Autowired
    private IPushWhiteListService pushWhiteListService;

    @Autowired
    private IWhiteListService whiteListService;


    @Autowired
    private IDeviceService deviceService;

    @Autowired
    private IInstitutionService institutionService;


    @Autowired
    private IReservationApplicationService reservationApplicationService;

    @Autowired
    private IAccompanyingService accompanyingService;


    @Override
    public String getToken(String ip, String port) {
        String sjyToken = "sjyToken";

        String token = (String) redisUtil.get(sjyToken);
        if (StringUtils.isNotBlank(token)) {
            return token;
        }
        HashMap<String, String> headers = new HashMap<>(16);
        String s = key + DateUtils.getYyyyMMdd(new Date());
        String md5Str = DigestUtils.md5DigestAsHex(s.getBytes());
        headers.put("requestKey", md5Str);

        Map<String, Object> map = new HashMap(16);
        map.put("userName", userName);
        map.put("password", password);
        JSON parse = JSONUtil.parse(map);

        String url = "http://" + ip + ":" + port + GET_TOKEN;

        log.info("[{}]ip：", ip);
        log.info("[{}]port：", port);
        log.info("[{}]url：", url);

        String result = HttpUtil.createPost(url).body(parse).addHeaders(headers).execute().body();

        log.info("[{}]result++", result);
        ResultSjy resultSjy = JSONUtil.toBean(result, ResultSjy.class);
        if (resultSjy.getCode() != 0) {
            throw new JeecgBootException("获取token失败");
        }

        Map<String, Object> date = resultSjy.getData();
        token = (String) date.get("token");
        if (StringUtils.isNotBlank(token)) {
            //  5400 等于一个半小时
            redisUtil.set("sjyToken", token, 5400);
            return token;

        } else {
            throw new JeecgBootException("外部系统未返回token");
        }

    }


    @Override
    public Boolean applicationDataDistribution() {

        List<PushRecord> pushRecords = pushRecordService.listPushRecordByStatusAll();

        if (CollectionUtils.isEmpty(pushRecords)) {
            return true;
        }
        log.info("[{}]pushRecords++", pushRecords.size());

        //已设备编码分组
        Map<String, List<PushRecord>> collect = pushRecords.stream().collect(Collectors.groupingBy(e -> e.getDeviceCode()));

        ExecutorService executor = Executors.newFixedThreadPool(5);
        collect.forEach((e, k) ->
                executor.submit(() ->
                        pushApplication(k)
                ));

        return true;
    }

    @Override
    public Boolean whiteListDataDistribution() {
        LambdaQueryWrapper<PushWhiteList> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(PushWhiteList::getPushStatus, 0, 2);
        List<PushWhiteList> list = pushWhiteListService.list(queryWrapper);

        if (CollectionUtils.isEmpty(list)) {
            return true;
        }

        //已设备编码分组
        Map<String, List<PushWhiteList>> collect = list.stream().collect(Collectors.groupingBy(e -> e.getType() + e.getDeviceCode()));
        TreeMap<String, List<PushWhiteList>> treeMap = new TreeMap(collect);

        //添加推送记录
        ExecutorService executor = Executors.newFixedThreadPool(5);
        treeMap.forEach((e, k) ->
                executor.submit(() ->
                        pushWhiteToSjy(k))
        );

        return true;
    }

    @Override
    @SneakyThrows
    public void pushWhiteToSjy(List<PushWhiteList> k) {
        if (k.get(0).getType() == SAVE_TYPE) {
            //添加
            pushSaveWhiteList(k);

        } else if (k.get(0).getType() == UPDATE_TYPE) {
            //修改
            pushUpdateWhiteList(k);

        } else if (k.get(0).getType() == DELETE_TYPE) {
            //删除
            pushDeleteWhite(k);
        }
    }

    private void pushDeleteWhite(List<PushWhiteList> k) {
        PushWhiteList pushRecord = k.get(0);
        for (PushWhiteList record : k) {
            Long pushId = record.getPushId();
            if (pushId == null) {
                PushWhiteList byPushWhiteListIdSave = pushWhiteListService.getByPushWhiteListIdSave(record.getWhiteListId());
                if (byPushWhiteListIdSave == null) {
                    continue;
                } else {
                    pushId = byPushWhiteListIdSave.getPushId();
                }
            }

            ResultSjy resultSjy = deletedWhiteList(pushId, pushRecord.getDeviceIp(), pushRecord.getDevicePort());
            PushWhiteList pushWhiteList = new PushWhiteList();
            pushWhiteList.setId(record.getId());
            pushWhiteList.setPushId(pushId);

            Integer pushStatus = resultSjy.getCode() == 0 ? 1 : 2;

            pushWhiteList.setPushStatus(pushStatus);

            pushWhiteListService.updateById(pushWhiteList);
        }
    }

    private void pushUpdateWhiteList(List<PushWhiteList> k) throws Exception {
        PushWhiteList pushRecord = k.get(0);
        for (PushWhiteList record : k) {
            WhiteList byId = whiteListService.getById(record.getWhiteListId());
            ReservationApplicationPushDTO reservationApplicationPushDTO = new ReservationApplicationPushDTO();
            Long pushId = record.getPushId();
            if (pushId == null) {
                PushWhiteList byPushWhiteListIdSave = pushWhiteListService.getByPushWhiteListIdSave(record.getWhiteListId());
                //编辑时查询没有添加的记录  则这次编辑作为第一次添加条件添加
                if (byPushWhiteListIdSave == null) {
                    List<PushWhiteList> pushSaveWhite = new ArrayList();
                    pushSaveWhite.add(record);
                    pushSaveWhiteList(pushSaveWhite);
                    continue;
                } else {
                    pushId = byPushWhiteListIdSave.getPushId();
                }
            }
            reservationApplicationPushDTO.setId(pushId);
            reservationApplicationPushDTO.setName(byId.getWhiteName());
            reservationApplicationPushDTO.setMobile(byId.getWhiteMobile());
            reservationApplicationPushDTO.setIdentityValue(byId.getIdentityValue());
            reservationApplicationPushDTO.setIdentityType(byId.getIdentityType());
            reservationApplicationPushDTO.setSex(byId.getSex());
            reservationApplicationPushDTO.setInstitutionCode(record.getInstitutionCode());
            reservationApplicationPushDTO.setDeviceCode(record.getDeviceCode());
            reservationApplicationPushDTO.setFaceImage(UploadUtil.getBaseById(GET_BASE + byId.getFacePhoto()));
            reservationApplicationPushDTO.setNumberPlate(byId.getNumberPlate());
            //todo 白名单是否都为绿码？
            reservationApplicationPushDTO.setCodeColour(1);
            //默认为白名单
            reservationApplicationPushDTO.setPersonType(2);

            //数据加密
            reservationApplicationPushDTO.setMobile(AESSJY.AESjiami(byId.getWhiteMobile()));
            reservationApplicationPushDTO.setIdentityValue(AESSJY.AESjiami(byId.getIdentityValue()));
            if (StringUtils.isNotBlank(byId.getNumberPlate())) {
                reservationApplicationPushDTO.setNumberPlate(AESSJY.AESjiami(byId.getNumberPlate()));
            }

            //调用接口
            ResultSjy resultSjy = upadteWhiteList(reservationApplicationPushDTO, pushRecord.getDeviceIp(), pushRecord.getDevicePort());

            //返回结果处理
            PushWhiteList pushWhiteList = new PushWhiteList();
            pushWhiteList.setId(record.getId());
            pushWhiteList.setPushId(pushId);

            Integer pushStatus = resultSjy.getCode() == 0 ? 1 : 2;

            pushWhiteList.setPushStatus(pushStatus);

            pushWhiteListService.updateById(pushWhiteList);
        }
    }

    private void pushSaveWhiteList(List<PushWhiteList> k) throws Exception {
        Date date = DateUtils.getDate();
        Date endCenturyDate = DateUtils.getEndCentury(date);
        List<ReservationApplicationPushDTO> reservationApplicationPushDTOS = new ArrayList();
        PushWhiteList pushRecord = k.get(0);
        for (PushWhiteList record : k) {
            WhiteList byId = whiteListService.getById(record.getWhiteListId());
            ReservationApplicationPushDTO reservationApplicationPushDTO = new ReservationApplicationPushDTO();
            reservationApplicationPushDTO.setSignId(record.getId());
            reservationApplicationPushDTO.setName(byId.getWhiteName());

            reservationApplicationPushDTO.setIdentityType(byId.getIdentityType());
            reservationApplicationPushDTO.setSex(byId.getSex());
            reservationApplicationPushDTO.setInstitutionCode(record.getInstitutionCode());
            reservationApplicationPushDTO.setDeviceCode(record.getDeviceCode());
            reservationApplicationPushDTO.setFaceImage(UploadUtil.getBaseById(GET_BASE + byId.getFacePhoto()));
            //todo 白名单是否都为绿码？
            reservationApplicationPushDTO.setCodeColour(1);
            //默认为白名单
            reservationApplicationPushDTO.setPersonType(2);
            //数据加密
            reservationApplicationPushDTO.setMobile(AESSJY.AESjiami(byId.getWhiteMobile()));
            reservationApplicationPushDTO.setIdentityValue(AESSJY.AESjiami(byId.getIdentityValue()));
            if (StringUtils.isNotBlank(byId.getNumberPlate())) {
                reservationApplicationPushDTO.setNumberPlate(AESSJY.AESjiami(byId.getNumberPlate()));
            }
            reservationApplicationPushDTO.setVisitStartDate(date);
            reservationApplicationPushDTO.setVisitEndDate(endCenturyDate);

            reservationApplicationPushDTOS.add(reservationApplicationPushDTO);


        }

        //调用接口
        ResultSjy resultSjy = pushVisitor(reservationApplicationPushDTOS, pushRecord.getDeviceCode(), pushRecord.getDeviceIp(), pushRecord.getDevicePort());

        //返回结果处理
        if (resultSjy.getCode() == 0 || resultSjy.getCode() == 1) {
            List<Success> successList = JSONObject.parseArray(resultSjy.getData().get("success").toString(), Success.class);

            List<PushWhiteList> recordList = new ArrayList();
            for (Success success : successList) {
                PushWhiteList record = new PushWhiteList();
                record.setId(success.getSignId());
                record.setPushId(success.getId());
                record.setPushStatus(1);
                recordList.add(record);
            }

            List<Success> failList = JSONObject.parseArray(resultSjy.getData().get("fail").toString(), Success.class);
            for (Success success : failList) {
                PushWhiteList record = new PushWhiteList();
                record.setId(success.getSignId());
                record.setPushId(success.getId());
                record.setPushStatus(2);
                recordList.add(record);
            }

            pushWhiteListService.updateBatchById(recordList);
        }

    }

    @Override
    @SneakyThrows
    public void pushApplication(List<PushRecord> k) {

        List<ReservationApplicationPushDTO> reservationApplicationPushDTOS = new ArrayList();

        PushRecord pushRecord = k.get(0);
        Institution institution = institutionService.getById(pushRecord.getInstitutionId());
        int reserveStartMin = institution.getReserveStartMin() == null ? 30 : institution.getReserveStartMin();
        int reserveEndMin = institution.getReserveEndMin() == null ? 30 : institution.getReserveEndMin();
        for (PushRecord record : k) {
            ReservationApplication byId = reservationApplicationService.getById(record.getReservationApplicationId());
            ReservationApplicationPushDTO reservationApplicationPushDTO = new ReservationApplicationPushDTO();
            BeanUtils.copyProperties(byId, reservationApplicationPushDTO);
            reservationApplicationPushDTO.setFaceImage(UploadUtil.getBaseById(GET_BASE + byId.getFacePhoto()));
            reservationApplicationPushDTO.setSignId(record.getId());
            reservationApplicationPushDTO.setDeviceCode(record.getDeviceCode());
            reservationApplicationPushDTO.setInstitutionCode(institution.getCode());

            reservationApplicationPushDTO.setIsCarStatus(record.getIsCarStatus());
            //默认为访客
            reservationApplicationPushDTO.setPersonType(byId.getVisitorIdentity());
            //能够进去的时间
            if (ReserveTypeEnum.SXW.getIndex().equals(institution.getReserveType())) {
                reservationApplicationPushDTO.setVisitStartDate(DateUtils.getMorningAndAfternoonStartWorkTime(byId.getVisitDate()));
                reservationApplicationPushDTO.setVisitEndDate(DateUtils.getMorningAndAfternoonEndWorkTime(byId.getVisitDate()));
            } else if (ReserveTypeEnum.QT.getIndex().equals(institution.getReserveType())) {
                reservationApplicationPushDTO.setVisitStartDate(DateUtils.getStartTime(byId.getVisitDate()));
                reservationApplicationPushDTO.setVisitEndDate(DateUtils.getEndTime(byId.getVisitDate()));
            } else {
                reservationApplicationPushDTO.setVisitStartDate(DateUtils.addMinTime(byId.getVisitDate(), -reserveStartMin));
                reservationApplicationPushDTO.setVisitEndDate(DateUtils.addMinTime(byId.getVisitDate(), reserveEndMin));
            }

            //数据加密
            reservationApplicationPushDTO.setIdentityValue(AESSJY.AESjiami(reservationApplicationPushDTO.getIdentityValue()));
            reservationApplicationPushDTO.setMobile(AESSJY.AESjiami(reservationApplicationPushDTO.getMobile()));
            reservationApplicationPushDTO.setRespondentMobile(AESSJY.AESjiami(reservationApplicationPushDTO.getRespondentMobile()));
            if (StringUtils.isNotBlank(reservationApplicationPushDTO.getNumberPlate())) {
                reservationApplicationPushDTO.setNumberPlate(AESSJY.AESjiami(reservationApplicationPushDTO.getNumberPlate()));
            }

            if (byId.getAccompanyingStatus() == 1) {
                List<Accompanying> accompanyings = accompanyingService.listAccompanyingByApplicationId(byId.getId());
                List<AccompanyingPushDTO> accompanyingPushDTOS = VisitorBeanUtils.convertList2List(accompanyings, AccompanyingPushDTO.class);

                for (AccompanyingPushDTO accompanyingPushDTO : accompanyingPushDTOS) {
                    //数据加密
                    accompanyingPushDTO.setIdentityValue(AESSJY.AESjiami(accompanyingPushDTO.getIdentityValue()));
                    accompanyingPushDTO.setMobile(AESSJY.AESjiami(accompanyingPushDTO.getMobile()));
                }

                reservationApplicationPushDTO.setAccompanys(accompanyingPushDTOS);
            }
            reservationApplicationPushDTOS.add(reservationApplicationPushDTO);
        }

        ResultSjy resultSjy = pushVisitor(reservationApplicationPushDTOS, pushRecord.getDeviceCode(), pushRecord.getDeviceIp(), pushRecord.getDevicePort());

        if (resultSjy.getCode() == 0 || resultSjy.getCode() == 1) {
            List<Success> successList = JSONObject.parseArray(resultSjy.getData().get("success").toString(), Success.class);

            List<PushRecord> recordList = new ArrayList();
            for (Success success : successList) {
                PushRecord record = new PushRecord();
                record.setId(success.getSignId());
                record.setPushId(success.getId());
                record.setPushStatus(1);
                recordList.add(record);
            }

            List<Success> failList = JSONObject.parseArray(resultSjy.getData().get("fail").toString(), Success.class);
            for (Success success : failList) {
                PushRecord record = new PushRecord();
                record.setId(success.getSignId());
                record.setPushId(success.getId());
                record.setPushStatus(2);
                recordList.add(record);
            }
            pushRecordService.updateBatchById(recordList);
        }
    }

    @Override
    public ResultSjy pushVisitor(List<ReservationApplicationPushDTO> reservationApplicationPushDTOS, String code, String ip, String port) {
        Map<String, Object> map = new HashMap(16);
        map.put("visitors", reservationApplicationPushDTOS);
        map.put("deviceCode", code);
        JSON parse = JSONUtil.parse(map);

        //存放请求头，可以存放多个请求头
        HashMap<String, String> headers = new HashMap<>(16);
        headers.put("token", getToken(ip, port));

        String url = "http://" + ip + ":" + port + VISITOR;

        log.info("[{}]code：", code);
        log.info("[{}]ip：", ip);
        log.info("[{}]port：", port);
        log.info("[{}]url：", url);
        String result = HttpUtil.createPost(url).body(parse).addHeaders(headers).timeout(10000).execute().body();

        log.info("[{}]resultSjy++++++", result);

        return JSONUtil.toBean(result, ResultSjy.class);
    }

    @Override
    public Boolean getHeartbeat(Integer id) {
        Device byId = deviceService.getById(id);
        String url = "http://" + byId.getDeviceIp() + ":" + byId.getDevicePort() + HEARTBEAT;


        String result = HttpUtil.createGet(url).execute().body();

        ResultSjy resultSjy = JSONUtil.toBean(result, ResultSjy.class);
        log.info("[{}]resule ++", result);

        Device device = new Device();
        device.setId(byId.getId());
        if (null != resultSjy && resultSjy.getCode() == 0) {
            device.setStatus(1);
            device.setUpdateTime(new Date());
            deviceService.updateById(device);
            return true;
        } else {
            device.setStatus(2);
            device.setUpdateTime(new Date());
            deviceService.updateById(device);
            return false;
        }

    }

    @Override
    public Boolean updateDeviceByHeartbeat() {
        LambdaQueryWrapper<Device> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Device::getDeleted, 1);
        List<Device> list = deviceService.list(queryWrapper);
        for (Device device : list) {
            String url = "http://" + device.getDeviceIp() + ":" + device.getDevicePort() + HEARTBEAT;


            String result = HttpUtil.createGet(url).execute().body();

            ResultSjy resultSjy = JSONUtil.toBean(result, ResultSjy.class);
            log.info("[{}]resule ++", result);
            if (null != resultSjy && resultSjy.getCode() == 0) {
                device.setStatus(1);
            } else {
                device.setStatus(2);
            }
            device.setUpdateTime(new Date());
            deviceService.updateById(device);

        }
        return true;

    }

    /**
     * 修改白名单信息
     *
     * @param reservationApplicationPushDTO
     * @param ip
     * @param port
     * @return
     */
    public ResultSjy upadteWhiteList(ReservationApplicationPushDTO reservationApplicationPushDTO, String ip, String port) {
        JSON parse = JSONUtil.parse(reservationApplicationPushDTO);

        //存放请求头，可以存放多个请求头
        HashMap<String, String> headers = new HashMap<>(16);
        headers.put("token", getToken(ip, port));

        String url = "http://" + ip + ":" + port + UPDATE;

        String result = HttpRequest.put(url).addHeaders(headers).body(parse).execute().body();
        log.info("[{}]result++", result);

        return JSONUtil.toBean(result, ResultSjy.class);
    }

    /**
     * 删除白名单信息
     *
     * @param pushId
     * @param ip
     * @param port
     * @return
     */
    public ResultSjy deletedWhiteList(Long pushId, String ip, String port) {
        //存放请求头，可以存放多个请求头
        HashMap<String, String> headers = new HashMap<>(16);
        headers.put("token", getToken(ip, port));


        String url = "http://" + ip + ":" + port + DELETE + pushId;

        String result = HttpRequest.delete(url).addHeaders(headers).execute().body();
        log.info("[{}]result++", result);
        return JSONUtil.toBean(result, ResultSjy.class);
    }
}
